﻿using Abp.Logging;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;

namespace AVIS.OutBound.WizardAgent.Helpers
{
    /// <summary>
    /// 参考地址：http://www.cnblogs.com/liqingwen/archive/2017/04/06/6672452.html
    /// </summary>
    public class RedisHelper
    {
        /// <summary>
        /// 默认的 Key 值（用来当作 RedisKey 的前缀）
        /// </summary>
        private static string DefaultKey = ConfigurationManager.AppSettings["OTA.Redis.DefaultKey"];
        private static Lazy<RedisHelper> _instance = new Lazy<RedisHelper>(() => new RedisHelper());
        public static RedisHelper Instance => _instance.Value;

        private RedisHelper()
        {
            try
            {
                var connectionString = ConfigurationManager.ConnectionStrings["Abp.Redis.Cache"].ConnectionString;
                Connection = ConnectionMultiplexer.Connect(connectionString);
                Database = Connection.GetDatabase();
            }
            catch (StackExchange.Redis.RedisConnectionException ex)
            {
                LogHelper.Logger.Error("Redis链接异常：", ex);
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("Redis初始化异常", ex);
            }
        }
        public ConnectionMultiplexer Connection { get; private set; }
        public IDatabase Database { get; private set; }

        #region String 操作

        /// <summary>
        /// 设置 key 并保存字符串（如果 key 已存在，则覆盖值）
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="redisValue"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static bool StringSet(string redisKey, string redisValue, TimeSpan? expiry = null)
        {
            try
            {
                redisKey = AddKeyPrefix(redisKey);
                return RedisHelper.Instance.Database.StringSet(redisKey, redisValue, expiry);
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("StringSet(string redisKey, string redisValue, TimeSpan? expiry = null)异常", ex);
            }
            return false;
        }

        /// <summary>
        /// 获取字符串
        /// </summary>
        /// <param name="redisKey"></param>
        /// <returns></returns>
        public static string StringGet(string redisKey)
        {
            try
            {
                redisKey = AddKeyPrefix(redisKey);
                return RedisHelper.Instance.Database.StringGet(redisKey);
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("StringGet(string redisKey)异常", ex);
            }
            return string.Empty;
        }

        /// <summary>
        /// 存储一个对象（该对象会被序列化保存）
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="redisValue"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static bool StringSet<T>(string redisKey, T redisValue, TimeSpan? expiry = null)
        {
            try
            {
                redisKey = AddKeyPrefix(redisKey);
                var json = Serialize(redisValue);
                return RedisHelper.Instance.Database.StringSet(redisKey, json, expiry);
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("StringSet<T>(string redisKey, T redisValue, TimeSpan? expiry = null)异常", ex);
            }
            return false;
        }

        /// <summary>
        /// 获取一个对象（会进行反序列化）
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static T StringGet<T>(string redisKey)
        {
            try
            {
                redisKey = AddKeyPrefix(redisKey);
                return Deserialize<T>(RedisHelper.Instance.Database.StringGet(redisKey));
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("StringGet<T>(string redisKey)异常", ex);
            }
            return default(T);
        }

        #endregion String 操作

        #region key 操作

        /// <summary>
        /// 移除指定 Key
        /// </summary>
        /// <param name="redisKey"></param>
        /// <returns></returns>
        public static bool KeyDelete(string redisKey)
        {
            try
            {
                redisKey = AddKeyPrefix(redisKey);
                return RedisHelper.Instance.Database.KeyDelete(redisKey);
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("KeyDelete(string redisKey)异常", ex);
            }
            return false;
        }

        /// <summary>
        /// 移除指定 Key
        /// </summary>
        /// <param name="redisKeys"></param>
        /// <returns></returns>
        public static long KeyDelete(IEnumerable<string> redisKeys)
        {
            try
            {
                var keys = redisKeys.Select(x => (RedisKey)AddKeyPrefix(x));
                return RedisHelper.Instance.Database.KeyDelete(keys.ToArray());
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("KeyDelete(IEnumerable<string> redisKeys)异常", ex);
            }
            return 0;
        }

        /// <summary>
        /// 校验 Key 是否存在
        /// </summary>
        /// <param name="redisKey"></param>
        /// <returns></returns>
        public static bool KeyExists(string redisKey)
        {
            try
            {
                redisKey = AddKeyPrefix(redisKey);
                return RedisHelper.Instance.Database.KeyExists(redisKey);
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("KeyExists(string redisKey)异常", ex);
            }
            return false;
        }

        /// <summary>
        /// 重命名 Key
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="redisNewKey"></param>
        /// <returns></returns>
        public static bool KeyRename(string redisKey, string redisNewKey)
        {
            try
            {
                redisKey = AddKeyPrefix(redisKey);
                return RedisHelper.Instance.Database.KeyRename(redisKey, redisNewKey);
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("KeyRename(string redisKey, string redisNewKey)异常", ex);
            }
            return false;
        }

        /// <summary>
        /// 设置 Key 的时间
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static bool KeyExpire(string redisKey, TimeSpan? expiry)
        {
            try
            {
                redisKey = AddKeyPrefix(redisKey);
                return RedisHelper.Instance.Database.KeyExpire(redisKey, expiry);
            }
            catch (Exception ex)
            {
                LogHelper.Logger.Error("KeyExpire(string redisKey, TimeSpan? expiry)异常", ex);
            }
            return false;
        }

        #endregion key 操作

        #region private method

        /// <summary>
        /// 添加 Key 的前缀
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private static string AddKeyPrefix(string key)
        {
            return $"{DefaultKey}:{key}";
        }

        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private static byte[] Serialize(object obj)
        {
            if (obj == null)
                return null;

            var binaryFormatter = new BinaryFormatter();
            using (var memoryStream = new MemoryStream())
            {
                binaryFormatter.Serialize(memoryStream, obj);
                var data = memoryStream.ToArray();
                return data;
            }
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        private static T Deserialize<T>(byte[] data)
        {
            if (data == null)
                return default(T);

            var binaryFormatter = new BinaryFormatter();
            using (var memoryStream = new MemoryStream(data))
            {
                var result = (T)binaryFormatter.Deserialize(memoryStream);
                return result;
            }
        }

        #endregion private method
    }
}
